package com.paradogs.gs.misc;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.reflect.GenericTypeUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.paradogs.common.utils.ObjectUtils;
import com.paradogs.framework.database.ParadogsDBAsyncThread;
import com.paradogs.gs.misc.annotations.GameManager;
import com.paradogs.gs.misc.annotations.NetSyncOwner;
import com.paradogs.gs.modules.GameObject;
import com.paradogs.gs.modules.Player;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.stream.Stream;

/**
 * @author: yumi
 * @date: 2024/10/15  18:42
 * @Description: TODO
 */
@Slf4j
public abstract class DBSingleRowManager<D extends ServiceImpl<? extends BaseMapper<E>, E>, E extends BaseMgrEntity> implements IDataManager<E, E> {

    @Getter
    protected GameObject gameObject;

    @Autowired
    private D dao;

    private E data;

    private Class<E> dataClass;

    private Field[] dataFieldsCache;

    private GameManager gameManager;

    @Setter
    private boolean dirty;

    public DBSingleRowManager() {
        // 获得泛型的 dao 类型, By Mybatis-Plus-Tools
        Class<?>[] classes = GenericTypeUtils.resolveTypeArguments(getClass(), DBSingleRowManager.class);
        dataClass = (Class<E>) classes[1];
        // 缓存数据反射 待优化缓存
        dataFieldsCache = Stream.concat(
                Arrays.stream(dataClass.getDeclaredFields()),
                Arrays.stream(dataClass.getSuperclass().getDeclaredFields())
        ).toArray(Field[]::new);
        for (Field field : dataFieldsCache) { field.setAccessible(true); }
        // 加载注解
        gameManager = AnnotationUtils.findAnnotation(this.getClass(), GameManager.class);
    }

    @Override
    public void loadData(GameObject gameObject) {
        try {
            this.gameObject = gameObject;
            this.data = dao.getById(gameObject.getId());
            if (this.data == null) {
                setDirty(true);
                this.data = dataClass.newInstance();
                this.data.setUid(gameObject.getId());
                onInitialization(this.data);
                this.setDirty(true);
            }
            this.data.setUid(gameObject.getId());
        } catch (Exception e) {
            log.error("{}", e.getMessage(), e);
        }
    }

    @Override
    public void save2DB() {
        if (!dirty) { return; }
        ParadogsDBAsyncThread.join(owner().getId(), () -> {
            try {
                data.setUid(owner().getId());
                if (!dao.updateById(data)) {
                    dao.save(data);
                }
            } catch (Exception e) {
                log.error("save error: {}, data: {}", e.getMessage(), data);
            }
        });
        dirty = false;
    }

    @Override
    public E __get__() {
        return ObjectUtils.clone(this.data);
    }

    @Override
    public E __raw__() {
        return this.data;
    }

    @Override
    public void __update__(E data) {
        this.data.setUid(owner().getId());
        if (data == null) { return; }
        // 自动状态同步
        try {
            E sync = null;
            for (int i = 0; i < dataFieldsCache.length; i++) {
                Field field = dataFieldsCache[i];
                Object nnew = field.get(data);
                if (this.data != null) { // 首次加载全量更新
                    Object old = field.get(this.data);
                    // 找出实际的差异数据
                    if (nnew == null && old == null
                            || nnew != null && nnew.equals(old)
                            || old  != null && old.equals(nnew)) {
                        continue;
                    }
                }

                // 设置脏数据标志位
                this.dirty = true;
                if (!gameManager.sync()) {
                    break;
                }

                // 设置同步数据
                NetSyncOwner netSyncOwner = AnnotationUtils.findAnnotation(field.getType(), NetSyncOwner.class);
                if (netSyncOwner != null && netSyncOwner.value() == false) { continue; }
                if (sync == null) {
                    sync = dataClass.newInstance();
                }
                field.set(sync, nnew);

            }
            // 提交到网络状态同步管理
            if (gameManager.sync()) {
                SyncDataUpdater.commit(owner().getId(), sync);
            }

            this.data = data;

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    @Override
    public void Destroy() {
        this.save2DB();
    }

    public Player owner() {
        return (Player) gameObject;
    }

    @Override
    public void syncAll() {
        try {
            E sync = dataClass.newInstance();
            for (int i = 0; i < dataFieldsCache.length; i++) {
                Field field = dataFieldsCache[i];
                // 设置同步数据
                NetSyncOwner netSyncOwner = AnnotationUtils.findAnnotation(field.getType(), NetSyncOwner.class);
                if (netSyncOwner != null && netSyncOwner.value() == false) { continue; }
                if (sync == null) {
                    sync = dataClass.newInstance();
                }
                field.set(sync, field.get(this.data));

            }
            // 提交到状态同步管理
            SyncDataUpdater.commit(owner().getId(), sync);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
